package com.cai.seckill.util.rabbitMQ;

import cn.hutool.json.JSONUtil;
import com.cai.seckill.common.vo.GoodsVo;
import com.cai.seckill.config.RabbitMQConfig;
import com.cai.seckill.pojo.SeckillOrder;
import com.cai.seckill.pojo.SeckillMessage;
import com.cai.seckill.pojo.User;
import com.cai.seckill.service.GoodsService;
import com.cai.seckill.service.OrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 消息消费者
 *
 * @BelongsProject secKill
 * @Author Uli1024
 * @CreaterTime 2022/8/6 22:20
 */
@Service
@Slf4j
public class MQReceiver {

    @Autowired
    private GoodsService goodsService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private OrderService orderService;

    /**
     * 下单操作
     *
     * @param message
     */
    //@RabbitListener(queues = "seckillQueue")
    //public void receive(String message, Channel channel, Message m) throws Exception {
    //    log.info("接收消息：" + message);
    //    SeckillMessage seckillMessage = JSONUtil.toBean(message, SeckillMessage.class);
    //    Long goodsId = seckillMessage.getGoodsId();
    //    User user = seckillMessage.getUser();
    //    GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
    //    //没库存了直接返回了
    //    if (goodsVo.getStockCount() < 1) {
    //        return;
    //    }
    //    //判断是否重复抢购
    //    SeckillOrder seckillOrder =
    //            (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
    //    if (seckillOrder != null) {
    //        return;
    //    }
    //    //下单操作
    //    try {
    //        orderService.seckill(user, goodsVo);
    //    } catch (Exception e) {
    //        //报错，但是因为用户轮询返回的结果是为了防止用户一直轮询所以在redis打下标记
    //        redisTemplate.opsForValue().set("seckillException:" + user.getId() + ":" + goodsId, 1, 10, TimeUnit.SECONDS);
    //        //因为这里报错了但是库存已经预先减了，所以要把库存加回去
    //        redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
    //    } finally {
    //        log.info("receiver-------{}", channel);
    //        //防止继续接收多余的消息
    //        channel.basicNack(m.getMessageProperties().getDeliveryTag(), false, false);
    //        channel.close();
    //    }
    //}
    @RabbitListener(queues = RabbitMQConfig.QUEUE)
    public void receive(Message message, Channel channel) throws Exception {
        log.info("接收消息：" + new String(message.getBody()));

        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        SeckillMessage seckillMessage = JSONUtil.toBean(new String(message.getBody()), SeckillMessage.class);
        Long goodsId = seckillMessage.getGoodsId();
        User user = seckillMessage.getUser();
        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
        //没库存了直接返回了
        if (goodsVo.getStockCount() < 1) {
            return;
        }
        //判断是否重复抢购
        SeckillOrder seckillOrder =
                (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if (seckillOrder != null) {
            return;
        }
        //下单操作
        try {
            orderService.seckill(user, goodsVo);
            channel.basicAck(deliveryTag, true);
        } catch (Exception e) {
            //报错，但是因为用户轮询返回的结果是为了防止用户一直轮询所以在redis打下标记
            redisTemplate.opsForValue().set("seckillException:" + user.getId() + ":" + goodsId, 1, 10, TimeUnit.SECONDS);

            //因为这里报错了但是库存已经预先减了，所以要把库存加回去
            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);

            //拒绝接受，不重回队列
            //long deliveryTag（受到的消息的标签）,
            // boolean multiple（true：允许多条消息同时被签收）,
            // boolean requeue(如果设置为true，那么消息重回队列里面，broker会重新发送)
            channel.basicNack(deliveryTag, false, false);
        } finally {
            channel.close();
        }
    }
}
